(include-book "io-utilities" :dir :teachpacks)
(include-book "list-utilities" :dir :teachpacks)

(defun get-length-of-time (start end accumulator)
  (if (equal start end)
      accumulator
      (get-length-of-time (+ start .5) end (+ accumulator 1))
  )
)

(defun map-day-of-week-to-number (day-of-week)
  (case day-of-week
       (#\U 0)
       (#\M 1)
       (#\T 2)
       (#\W 3)
       (#\R 4)
       (#\F 5)
       (#\S 6)
       (otherwise day-of-week)
    )
)

(defun get-time-as-float (time)
  (let ((hour (chrs->str (car (break-at #\: time))))
        (minutes (chrs->str (cadr (break-at #\: time)))))
    (if (equal hour 12)
        (if (equal minutes ":30")
           (+ 0 .5)
           0
        )
        (if (equal minutes ":30")
           (+ (str->rat hour) .5)
           (str->rat hour)
        )
    )
  )
)

(defun get-length-of-range (range)
  (let* ((first (car (packets #\~ range)))
         (last (cadr (packets #\~ range)))
         (first-as-number (get-time-as-float (remove #\a (remove #\p (remove #\m first)))))
         (last-as-number (get-time-as-float (remove #\a (remove #\p (remove #\m last))))))
    (if (member #\a first)
        (if (member #\a last)
            (* 2 (- last-as-number first-as-number))
            (* 2 (- (+ 12 last-as-number) first-as-number))
        )
        (* 2 (- last-as-number first-as-number))
    )
  )
)

(defun get-length-and-start-of-range-helper (range)
  (if (member #\a (car (packets #\– range)))
      (cons (get-time-as-float (remove #\a (remove #\p (remove #\m (car (packets #\~ range)))))) 
            (get-length-of-range range))
      (cons (+ 12 (get-time-as-float (remove #\a (remove #\p (remove #\m (car (packets #\~ range)))))))
               (get-length-of-range range))
  )
)

(defun get-length-and-start-of-range (ranges)
  (if (consp ranges)
      (cons (get-length-and-start-of-range-helper (car ranges))
            (get-length-and-start-of-range (cdr ranges)))
      nil
      )                                        
)

(defun get-day-and-times-pair (student-time-as-chrs)
  (list (map-day-of-week-to-number (car student-time-as-chrs)) 
        (get-length-and-start-of-range (packets #\, (cdr student-time-as-chrs))))
)

(defun times-from-student-as-str-helper (student-times-as-chrs)
  (if (consp student-times-as-chrs)
      (cons (get-day-and-times-pair (car student-times-as-chrs))
            (times-from-student-as-str-helper (cdr student-times-as-chrs)))
      nil
  )
)

(defun replace (start end x xs)
  (let* ((first (car (break-at-nth start xs)))
         (second (cadr (break-at-nth (+ end 1) xs))))
         (append first (replicate (+ (- end start) 1) x) second)
  )
)

(defun get-part-of-day (day range array)
  (replace (+ (* day 48) (* 2 (car range))) (- (+ (cdr range) (+ (* day 48) (* 2 (car range)))) 1) 0 array)
)

(defun get-schedule-as-bit-array-helper (day ranges array)
  (if (consp ranges)
      (get-schedule-as-bit-array-helper day (cdr ranges) (get-part-of-day day (car ranges) array))
      array
  )
)

(defun get-schedule-as-bit-array (schedule array)
  (if (consp schedule)
      (get-schedule-as-bit-array (cdr schedule) 
                                (get-schedule-as-bit-array-helper (caar schedule) (cadar schedule) array))
      array
  )
)

(defun schedule-as-bit-array-from-student-as-str (student-as-string)
  (get-schedule-as-bit-array (times-from-student-as-str-helper
      (packets #\; (remove #\space (cadr (cddddr (packets #\| (str->chrs student-as-string)))))))
                             (replicate (* 48 7) 1))
)

(defun get-input-file-as-str (input)
  (mv-let (file-as-string error state)
            (file->string input state)
            (map-chrs->str 
               (remove nil (packets-set (list (code-char 10) (code-char 13)) (str->chrs file-as-string))))
  )
)

(defun student-to-data (student)
  (let ((student-packet (packets #\| (str->chrs student))))
       (list (car student-packet)  (cadr student-packet) (caddr student-packet) (cadddr student-packet)
             (car (cddddr student-packet)) (schedule-as-bit-array-from-student-as-str student))
  )
)

(defun get-students-as-data-helper (students)
  (if (consp students)
      (cons (student-to-data (car students)) 
            (get-students-as-data-helper (cdr students)))
      nil
  )
)

(defun get-students-as-data (input)
  (get-students-as-data-helper (cdr (get-input-file-as-str input)))
)